#include <iostream>
#include <iomanip>
#include <csignal>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>

#include "IOError.h"
#include "DigitalIO_DL636F.h"
#include "Counter_DL636G1.h"
#include "TimeCounter_DL643A1.h"
#include "teebuf.h"

using namespace std;


// ---- global variables ---- //
char      *devfn             = "/tmp/sis1100_00remote";
u_int32_t io_addr            = 0x8800;
u_int32_t counter_addr       = 0x8840;
u_int32_t tc_addr            = 0x8860;
u_int32_t MIN_DELTA          = 100;
u_int32_t MAX_DELTA          = 500;
u_int32_t MAX_READ           = 0xfffff;

int                 crate    = -1;
DigitalIO_DL636F    *io      = NULL;
Counter_DL636G1     *counter = NULL;
TimeCounter_DL643A1 *tc      = NULL;

u_int32_t         loops      = 0;
u_int32_t         sleep_time = 100;

u_int32_t *tc_buf = 0;
u_int32_t *adc1_buf = 0;

double             rate      = 0; // in kHz
u_int32_t          last_read = 0;
u_int32_t          last_tc   = 0;

// ---- function declarations ---- //
void signal_handler(int signal);
void safe_exit(int r);

/*****************************************************************************/
int main(int argc,char *argv[])
/*****************************************************************************/
{
		signal(SIGINT,signal_handler);
		cerr.rdbuf(new teebuf(cerr.rdbuf(),cout.rdbuf()));

		cerr << "test_rate     built: " << __DATE__ << " " << __TIME__ << "\n\n"
				 << "to quit press CTRL-C!\n\n";

    tc_buf = new u_int32_t[MAX_DELTA*2];
		adc1_buf = new u_int32_t[MAX_DELTA];

		try {
				cerr << "initialization\n\n";

				if( !(crate=open(devfn, O_RDWR, 0)) )
				{
						cerr << "ERROR: could not open \"" << devfn << "\"" << endl;
						safe_exit(1);
				}

				cerr << "vmesysreset..." << flush;
				if(int r=vmesysreset(crate)) {
						cerr << "error: vmesysreset() returned " << r << endl << endl;
						cerr.flush(); exit(1);
				}
				sleep(1);	cerr << "ok\n";


				io      = new DigitalIO_DL636F(crate,io_addr);
				counter = new Counter_DL636G1(crate,counter_addr);
				tc      = new TimeCounter_DL643A1(crate,tc_addr);

				cerr << "reset tc..."; tc->reset(); cerr << "ok\n";
				cerr << "reset counter..."; counter->reset(); cerr << "ok\n";

				cerr << "measuring:\n\n";

				u_int32_t read = 0;

				time_t start = time(0);

				u_int32_t tc_words;
				u_int32_t counter_c;
				u_int32_t delta;
				io->setOutputs(0x1);
				while(read < MAX_READ) {
						tc_words  = tc->getWords();
						counter_c = counter->getC();

						usleep(sleep_time);

						delta     = tc_words;

						if(delta > MAX_DELTA) delta = MAX_DELTA;

						loops++;

						cerr << "\033[1A"
								 << "t: " << difftime(time(0),start) << "s"
								 << "  l: " << setw(6) << loops
								 << "  tcw: " << setw(4) << tc_words
								 << "  c: " << setw(8) << counter_c
								 << "  r: " << setw(8) << read
								 << "  (" << (int)((double)read*100 / MAX_READ) << "%)"
								 << "  d: " << delta 
								 << " rate: "
								 << setprecision(4) << setfill('0') << rate << "kHz  \n";

						if(delta < MIN_DELTA)
								continue;

						tc->readFIFO32DMA(delta*2,tc_buf);	read += delta;

						u_int32_t value;

						for(u_int32_t i=1; i<delta*2; i+=2) {
								if(tc_buf[i] & 0x8000) cerr << "tc: fifo empty\n\n";
								if(tc_buf[i] & 0x4000) cerr << "tc: fifo full\n\n";

								value		= (tc_buf[i]&0xffff)<<16 | (tc_buf[i-1]&0xffff);


//								cout << hex << value << endl;
						}

						if(value - last_tc > 100000) {
								if(value > last_tc)
										rate = ((double)(read-last_read))/(value-last_tc)*1000;
								else 
										rate = ((double)(read-last_read))
												/((0xffffffff-last_tc)+value)*1000;

								last_read = read;
								last_tc   = value;
						}
				}
				io->setOutputs(0);

				counter_c = counter->getC();

				cerr << "counter_c - read = " << counter_c - read << endl
						 << "tc_words = " << (tc_words=tc->getWords()) << endl << endl;

				if( (counter_c - read) != tc_words ) {
						cerr << "there is something wrong\n\n";
						safe_exit(3);
				}
				else
						cerr << "everything ok\n\n";

				cerr << "read the rest...";
				tc->readFIFO32DMA(tc_words*2,tc_buf);
				read += tc_words;
				cerr << "ok\n\n";

		} catch( IOError ioe ) {
				cerr << "catched IOError:\n"
						 << "  msg: " << ioe.getMsg() << "\n"
						 << "param: " << ioe.getParam()
						 << " == 0x" << hex << ioe.getParam()
				     << "\n";
				safe_exit(1);
		}

		safe_exit(0);
}
/*****************************************************************************/


/*---------------------------------------------------------------------------*/
void signal_handler(int signal)
/*---------------------------------------------------------------------------*/
{
		if(signal == SIGINT) {
				cerr << "\n\nCTRL-C pressed\n";
				safe_exit(2);
		} else
				psignal(signal, "signal_handler(): unknown signal!");
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void safe_exit(int r)
/*---------------------------------------------------------------------------*/
{
		cerr << "\nsafe_exit()\n";

    if(crate > -1 ) {
				cerr << "vmesysreset...";
				if(int r=vmesysreset(crate)) {
						cerr << "error: vmesysreset() returned " << r << endl << endl;
						cerr.flush(); exit(1);
				}
				sleep(1);	cerr << "ok\n";

				cerr << "closing crate\n\n"; close(crate);
		}

		exit(r);
}
/*---------------------------------------------------------------------------*/
